home *** CD-ROM | disk | FTP | other *** search
/ Aminet 3 / Aminet 3 - July 1994.iso / Aminet / dev / gui / EAGUI22.lha / EAGUI / Tutorial.doc < prev   
Encoding:
Text File  |  1994-05-21  |  27.8 KB  |  628 lines

  1.  
  2. -----------------------------------------------------------------------------
  3.  
  4.                  Environment Adaptive Graphic User Interface
  5.  
  6. -----------------------------------------------------------------------------
  7. $RCSfile: Tutorial.doc,v $
  8. $Revision: 2.4 $
  9. $Date: 1994/05/21 14:30:30 $
  10. -----------------------------------------------------------------------------
  11.  
  12.  
  13.      Introduction
  14.      ¯¯¯¯¯¯¯¯¯¯¯¯
  15. The Environment Adaptive Graphic User Interface (EAGUI) is a system which
  16. allows you to build interfaces that, as the name suggests, adapt to the
  17. environment they're run in. It uses normal GadTools and BOOPSI gadgets, and
  18. does not modify them in any way. This allows programmers to implement EAGUI
  19. in existing applications easily.
  20.  
  21. This tutorial is a practical introduction to EAGUI. After you've read this,
  22. you should be able to write your own interfaces, using the AutoDocs
  23. (EAGUI.doc) as a reference. Apart from reading this document, you might also
  24. want to take a look at the examples. The tutorial assumes you are familiar
  25. with programming the AmigaOS[2], and some knowledge of C is useful too,
  26. because all examples are written in C.
  27.  
  28. This tutorial is based on a simple example. The full source of this example
  29. can be found in "example.c".
  30.  
  31. Not every option is discussed here, so please study the AutoDocs carefully.
  32.  
  33.  
  34.      Concepts
  35.      ¯¯¯¯¯¯¯¯
  36. EAGUI divides the contents of a window in a hierarchical object tree. Objects
  37. can be either gadgets, images or groups. Groups can contain any number of
  38. objects. There are two types of groups: horizontal and vertical groups.
  39. Horizontal groups contain objects that are placed next to each other,
  40. vertical groups contain objects that are placed below each other. An object
  41. tree contains all information to create the gadgets and images in a window.
  42.  
  43. For example, let's look at a string requester window that contains a text
  44. label, a string gadget, and an "Ok" and "Cancel" button below it. Starting at
  45. the top of the tree, we define a vertical group to divide the window in two.
  46. The upper object will contain the text label, the middle one the string
  47. gadget and the lower one will contain the buttons. The lower object in turn
  48. is a horizontal object, which contains three (!) objects: the "Ok" button, an
  49. empty spaceholder and the "Cancel" button. The complete tree now looks like
  50. this:
  51.  
  52.      Vertical group
  53.           Text label
  54.           String gadget
  55.           Horizontal group
  56.                "Ok" button gadget
  57.                Empty spaceholder
  58.                "Cancel" button gadget
  59.  
  60. This tree already holds some information about how objects should be placed
  61. in the window, but not enough. Let's assume we want to create a resizable
  62. window, since this is clearly the most difficult type of window to maintain.
  63.  
  64. A window can't be arbitrarily small, or else the objects won't fit inside the
  65. window. There is a minimum size. This size is determined by looking at the
  66. minimum size for each individual object in the window. We start at the bottom
  67. of the tree, where we find three objects. It is obvious that the labels of
  68. the buttons have to fit within the frame. The minimum size of these objects
  69. therefore is determined by the dimensions of the label. The other object, the
  70. empty spaceholder, is merely an object that fills up the empty space between
  71. the "Ok" and "Cancel" gadgets. This space can be arbitrarily small, so its
  72. minimum sizes are zero. Going up in the tree, we arrive at the Horizontal
  73. group object. The minimum sizes of its members are already determined, so the
  74. minimum sizes of the group are obtained by adding the horizontal minimum
  75. sizes of all children and taking the largest vertical size of the children.
  76. The next object, the string gadget, also has a minimum size that is
  77. determined by the dimensions of the string that has to fit inside the gadget.
  78. Finally, going up again, we arrive at the vertical group. Again, its members
  79. are known, so determining the size of the group is easy.
  80.      All size calculations can be done at runtime. This is necessary because
  81. an application can not know in advance what font or locale the user has
  82. selected, and there is no valid reason why the user should be forced to use a
  83. certain font or locale. To allow flexible size calculations, each object uses
  84. a method to determine its minimum size. This method is implemented as a Hook.
  85. If you want, you can write this Hook function yourself, but for normal
  86. gadgets, general purpose methods are already supplied. More information about
  87. these Hooks can be found lateron.
  88.      Apart from the minimum sizes, objects have borders, whose sizes can also
  89. be determined using methods if you want. Borders can be seen as whitespace
  90. around objects.
  91.  
  92. It can sometimes be hard to remember the exact relation between the size,
  93. border size and offset of an object. Therefore, we've supplied a simple IFF
  94. picture, which shows the relations graphically. This picture's called
  95. "Tutorial_1.pic".
  96.  
  97. Now that we know the minimum sizes, we also know the minimum inner size of
  98. the window. Please note that to determine the minimum size of the window, we
  99. have to add the sizes of the window borders. This presents us with a small
  100. problem, since we do not know the thickness of the borders in advance. There
  101. are several ways of dealing with this problem. These will be discussed
  102. lateron.
  103.  
  104. The next step is to actually open the window. After we've done that, we
  105. actually know the inner sizes of the window, and therefore we know the size
  106. of the root (top) object, which, in our example, is the vertical group. The
  107. object is set to this size. From hereon, the sizes of all other object are
  108. calculated.
  109.  
  110. We've already mentioned the minimum sizes, but there is another important
  111. object attribute we've left out up til now. Suppose that the window is bigger
  112. than the minimum size: how do we divide the available space? Some objects
  113. will want to use as much space as possible, while others will want to remain
  114. the same size. To solve this problem, each object can have a weight. This is
  115. a factor, which indicates how an object will grow in relation to other
  116. objects in the same group. Let's return to our example.
  117.  
  118. The vertical group contains three objects. Each object has a vertical size,
  119. that really doesn't need to grow bigger. In practice, this means that the
  120. whole window doesn't need to be resizable in the vertical direction, only in
  121. the horizontal direction. The horizontal size of the objects is automatically
  122. the same as the window's inner width. Now we take a look at the button
  123. gadgets. These are grouped in the lower horizontal group. If this group
  124. becomes wider, the "Cancel" button should move to the right too. Looking at
  125. the three objects in this group, we should allow the middle one (i.e. the
  126. empty spaceholder) to grow bigger. We do this by setting its weight to one.
  127. Both buttons can have a fixed size, so their weights are zero. An alternative
  128. is to give the buttons a weight of one too. That means that the available
  129. horizontal space will be evenly divided between the three objects, because
  130. each object has the same weight. As you can see, this is a very simple, yet
  131. powerful concept.
  132.  
  133. One thing to keep in mind is that if a group contains _no_ weighed objects,
  134. it is not filled completely. This is perfectly legal, starting with release
  135. 2.2, and you don't need an empty object at the end of such a group.
  136.  
  137.  
  138.      Designing the interface
  139.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  140. The first thing you have to do, is make a sketch of the interface. You can
  141. either do this on a piece of paper, or by using some structured drawing tool.
  142. At this stage, you'll have to figure out how to divide the interface in
  143. groups of objects.
  144.  
  145. When you start designing an interface, make sure you've read The Amiga User
  146. Interface Style Guide[1]. It contains a lot of guidelines, and following
  147. these will make your interface easier to use.
  148.  
  149. Also, it is good practice to look at available software, and see how
  150. particular problems are solved. Especially the preference editors on your
  151. Workbench are good examples of how interfaces should look like (although they
  152. don't font adapt, unless you're a developer using a beta Release 3.1).
  153.  
  154.  
  155.      Using the interface
  156.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  157. Basically, there are three main services you must provide to work with a
  158. window. First, you must initialize everything. Then you must handle incoming
  159. messages, until at some point the window is forced to close again, which is
  160. the third and last service. This is pretty straightforward.
  161.  
  162. Let's look at the initialization first. Creating a new object is quite
  163. simple, and anybody who has used BOOPSI will be familiar with the type of
  164. function call. All you do is call ea_NewObjectA(). This function takes the
  165. object type as the first argument, and a pointer to a taglist as the second
  166. one. Alternatively, you can use ea_NewObject(), which allows you to pass the
  167. individual tags as arguments. If all went well, the function will return a
  168. pointer to the object, which can be used as a handle. It is not encouraged
  169. nor allowed to make assumptions about the data that the pointer points to.
  170. The ea_NewObjectA() function can be used to create a whole tree easily. For
  171. more information, look at the AutoDocs. Let's take another look at our
  172. example:
  173.  
  174.      LONG init(VOID)
  175.      {
  176.           if (!(winobj_ptr = ea_NewObject(EA_TYPE_VGROUP,
  177.              EA_Child,            ea_NewObject(EA_TYPE_CUSTOMIMAGE,
  178.                     [...]
  179.                  ),
  180.                EA_Child,      ea_NewObject(EA_TYPE_GTGADGET,
  181.                     EA_GTType,     STRING_KIND,
  182.                     [...]
  183.                     ),
  184.                EA_Child,      ea_NewObject(EA_TYPE_HGROUP,
  185.                     EA_Child,      ea_NewObject(EA_TYPE_GTGADGET,
  186.                          EA_GTType,     BUTTON_KIND,
  187.                          EA_GTText,     "Ok",
  188.                          [...]
  189.                          ),
  190.                     EA_Child,      ea_NewObject(EA_TYPE_CUSTOMIMAGE,
  191.                          [...]
  192.                          ),
  193.                     EA_Child,      ea_NewObject(EA_TYPE_GTGADGET,
  194.                          EA_GTType,     BUTTON_KIND,
  195.                          EA_GTText,     "Cancel",
  196.                          [...]
  197.                          ),
  198.                     [...]
  199.                     ),
  200.                [...]
  201.                )))
  202.           {
  203.                Printf("Couldn't create object tree.\n");
  204.                return(20);
  205.           }
  206.           return(0);
  207.      }
  208.  
  209. Some tags have been left out here, as marked by the ellipsis [...], because
  210. they are not essential to the example. Some other tags must be specified for
  211. GadTools or BOOPSI gadgets, and information on this can be found in the
  212. AutoDocs of these libraries. The most important thing is the way you can
  213. hierarchically define a tree. If something along the way goes wrong (which is
  214. usually because there wasn't enough memory) then nothing is created (all
  215. objects in the tree which were already created will be freed automatically),
  216. and NULL is returned.
  217.  
  218. Note that we have split the string gadget and its label in two seperate
  219. objects. We did this to demonstrate the custom image facilities implemented
  220. in EAGUI. Of course it is possible to let Gadtools place the label above
  221. the string gadget.
  222.  
  223. Now that we've initialized a tree, we must also have a way to clean it up.
  224. This is even simpler. All we need is a simple call to ea_DisposeObject().
  225. This removes an object and all its children. In our example, it would look
  226. something like this:
  227.  
  228.      VOID cleanup(VOID)
  229.      {
  230.           ea_DisposeObject(winobj_ptr);
  231.      }
  232.  
  233. The next step is to calculate the minimum sizes. If you want the window to be
  234. resizable, then you must set the window sizing limits with the WindowLimits()
  235. call. Before that, you must first obtain the minimum sizes. This can be done
  236. easily by using ea_GetAttrs() on the `winobj_ptr'. The following example
  237. explains all this:
  238.  
  239.      /* obtain the minimum sizes of every object in the tree */
  240.      ea_GetMinSizes(winobj_ptr);
  241.  
  242.      /* get some attributes */
  243.      ea_GetAttrs(winobj_ptr,
  244.           EA_MinWidth,        &w,
  245.           EA_MinHeight,       &h,
  246.           EA_BorderLeft,      &bl,
  247.           EA_BorderRight,     &br,
  248.           EA_BorderTop,       &bt,
  249.           EA_BorderBottom,    &bb,
  250.           TAG_DONE);
  251.  
  252.      /* open the window */
  253.      win_ptr = OpenWindowTags([...]);
  254.  
  255.      /* set the window limits */
  256.      WindowLimits(
  257.           win_ptr,
  258.           w + win_ptr->BorderLeft + win_ptr->BorderRight + bl + br,
  259.           h + win_ptr->BorderTop + win_ptr->BorderBottom + bt + bb,
  260.           ~0,
  261.           h + win_ptr->BorderTop + win_ptr->BorderBottom + bt + bb);
  262.  
  263.      /* fill in the inner sizes of the window in the root object */
  264.      ea_SetAttrs(winobj_ptr,
  265.           EA_Width,      win_ptr->Width -
  266.                          win_ptr->BorderLeft -
  267.                          win_ptr->BorderRight -
  268.                          bl -
  269.                          br,
  270.           EA_Height,     win_ptr->Height -
  271.                          win_ptr->BorderTop -
  272.                          win_ptr->BorderBottom -
  273.                          bt -
  274.                          bb,
  275.           EA_Left,       win_ptr->BorderLeft,
  276.           EA_Top,        win_ptr->BorderTop,
  277.           TAG_DONE);
  278.  
  279.      /* now determine the object sizes and positions */
  280.      ea_LayoutObjects(winobj_ptr);
  281.  
  282.      /* create the list of gadgets for this window */
  283.      rc = ea_CreateGadgetList(winobj_ptr, &gadlist_ptr,
  284.           visualinfo_ptr, drawinfo_ptr);
  285.  
  286.      /* add the gadgetlist to the window */
  287.     AddGList(win_ptr, gadlist_ptr, -1, -1, NULL);
  288.     RefreshGList(gadlist_ptr, win_ptr, NULL, -1);
  289.     GT_RefreshWindow(win_ptr, NULL);
  290.  
  291.     /* finally, we render the custom imagery, if there is any */
  292.     ea_RenderObjects(winobj_ptr, win_ptr->RPort);
  293.  
  294.      if (rc != 0)
  295.      {
  296.           /* bail out */
  297.           exit(20);
  298.      }
  299.  
  300.      /* now you're ready for business */
  301.  
  302. You can now handle events in exactly the same way you normally do. The only
  303. exception is the IDCMP_NEWSIZE event. This is where you can use EAGUI to
  304. adapt to the new window size. If you receive one of these, you must do the
  305. following things:
  306.  
  307.      a) Store any unsaved changes the user has made to the gadgets. This
  308. means storing strings that were entered in string gadgets, items that were
  309. selected in listviews, cycle gadgets or radio buttons, and things like that.
  310. Depending on your program structure, this may not be necessary at all,
  311. because the changes were already stored when the IDCMP message that notified
  312. that change was received.
  313.  
  314.      b) Remove the gadget list from the window, and clean it up, like this:
  315.  
  316.           RemoveGList(win_ptr, gadlist_ptr, -1);
  317.           ea_FreeGadgetList(winobj_ptr, gadlist_ptr);
  318.           gadlist_ptr = NULL;
  319.  
  320.      c) Examine the new dimensions of the window, and set the root object
  321. accordingly, like this:
  322.  
  323.           ea_GetAttrs(winobj_ptr,
  324.                EA_BorderLeft,      &bl,
  325.                EA_BorderRight,     &br,
  326.                EA_BorderTop,       &bt,
  327.                EA_BorderBottom,    &bb,
  328.                TAG_DONE);
  329.  
  330.           ea_SetAttrs(winobj_ptr,
  331.                EA_Width,           win_ptr->Width -
  332.                                    win_ptr->BorderLeft -
  333.                                    win_ptr->BorderRight -
  334.                                    bl -
  335.                                    br,
  336.                EA_Height,          win_ptr->Height -
  337.                                    win_ptr->BorderTop -
  338.                                    win_ptr->BorderBottom -
  339.                                    bt -
  340.                                    bb,
  341.                EA_Left,            win_ptr->BorderLeft,
  342.                EA_Top,             win_ptr->BorderTop,
  343.                TAG_DONE);
  344.  
  345.           ea_LayoutObjects(winobj_ptr);
  346.  
  347.      d) Build the gadget list again, and connect it to the window. Currently,
  348. the method I use to refresh the window looks somewhat strange. I haven't
  349. quite figured out what to do with it. It seems that GadTools refreshes the
  350. gadgets directly after resizing the window, but before you're notified of
  351. that fact. Therefore, it renders over your window borders. After that, you
  352. get a IDCMP_NEWSIZE message, and you have to redraw everything yourself
  353. (including the window borders). Finally redraw the custom images. This is
  354. example of working code:
  355.  
  356.           rc = ea_CreateGadgetList(winobj_ptr, &gadlist_ptr,
  357.                visualinfo_ptr, drawinfo_ptr);
  358.           if (rc != 0)
  359.           {
  360.                /* bail out */
  361.                exit(20);
  362.           }
  363.           EraseRect(win_ptr->RPort,
  364.                win_ptr->BorderLeft,
  365.                win_ptr->BorderTop,
  366.                win_ptr->Width - win_ptr->BorderRight - 1,
  367.                win_ptr->Height - win_ptr->BorderBottom - 1);
  368.  
  369.           RefreshWindowFrame(win_ptr);
  370.  
  371.           AddGList(win_ptr, gadlist_ptr, -1, -1, NULL);
  372.           RefreshGList(gadlist_ptr, win_ptr, NULL, -1);
  373.           GT_RefreshWindow(win_ptr, NULL);
  374.  
  375.           /* finally, we render the imagery, if there is any */
  376.          ea_RenderObjects(winobj_ptr, win_ptr->RPort);
  377.  
  378.  
  379.      Includes and Macros
  380.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  381. When using the library in your own program, there are certain headers that
  382. you need to include.
  383.      First, you must include "EAGUI.h", which contains all necessary
  384. information for using the library. This header then automatically includes
  385. "EAGUI_protos.h", which contains all needed function prototypes. There is one
  386. switch that may be of use. If you #define NOEAGUIMACROS before you include
  387. the header file, you won't get the macros in "EAGUI_macros.h", otherwise you
  388. will. For normal circumstances, the macros seem to work well, but you may
  389. have your own personalized set of macros that you're more comfortable with,
  390. so we won't force you to use them.
  391.      The second file you should include (at least when you're using SAS/C) is
  392. a pragmas file (called "EAGUI_pragmas.h"). Note that this file is not part of
  393. the distribution, because it is compiler specific, and can be generated from
  394. the "EAGUI.fd" file. Users of other languages may find the EAGUI.fd file
  395. useful too. If you've written header files for any other language, and you
  396. want us to include them in the EAGUI package, please contact us.
  397.  
  398.  
  399.      Standard Methods
  400.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  401. For all GadTools gadgets, minimum size and border methods are supplied in the
  402. library.
  403.  
  404. The methods that are supplied will try to adapt to all different tags that
  405. have an influence on the size of the object. The minimum size methods
  406. basically make the gadgets big enough to just fit. String gadgets for example
  407. will be high enough for the selected font, and wide enough for the border to
  408. be rendered succesfully. Border size methods will adapt to labels which are
  409. placed above, left of, right of or below a gadget. Please note that if, for
  410. example, you place a text above a gadget, the top border will only be high
  411. enough for the text to fit. If the text is _wider_ than the gadget, the
  412. gadget will not be adjusted: the label will simply be rendered, and you'll
  413. have to make sure that this doesn't conflict with other objects.
  414.  
  415. To use the standard methods, you must specify the EA_StandardMethod tag, like
  416. this:
  417.  
  418.      ea_NewObject([...]
  419.           EA_StandardMethod,  value,
  420.           [...]
  421.  
  422. The `value' parameter can be EASM_MINSIZE, EASM_BORDER or a combination of
  423. both (EASM_MINSIZE | EASM_BORDER), depending on what standard methods you
  424. want to use for this object. The EAGUI_macros.h file already uses this tag,
  425. so if you use these macros, the standard methods will be used automatically.
  426.  
  427.  
  428.      Creating your own Methods
  429.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  430. Although the standard methods will be sufficient in many cases, there might
  431. be times when you want to create your own methods. A good example would be
  432. when you use BOOPSI gadgets. Other reasons for creating your own methods
  433. could be that your interface uses gadgets of a much simpler form, or that it
  434. only uses certain types of gadgets. Custom methods might improve execution
  435. speed slightly, since they don't need to be `universal'.
  436.  
  437. When creating you own methods, many things are possible, but there are a few
  438. things you have to keep in mind.
  439.  
  440. Every method is called using a callback hook. For more information about
  441. initializing and using hooks, please refer to the RKRM's[2] or the
  442. <utility/hooks.h> header file.
  443.  
  444. A method prototype should look like this:
  445.  
  446.      ULONG method(
  447.           struct Hook *       hook_ptr,
  448.           struct ea_Object *  object_ptr,
  449.           APTR                message_ptr);
  450.  
  451. The hook_ptr contains the pointer to the hook structure of the method.
  452.  
  453. The object_ptr points to the object that the method should be applied to.
  454.  
  455. The message_ptr is not used at the moment.
  456.  
  457. The method can change the attributes of the object. We strongly discourage
  458. you to let the method change any other attributes than the ones it should
  459. set. So a border method should only set the border attributes of the object,
  460. and a minsize method should only set the minsize attributes. You are allowed
  461. to read other attributes, if you need them for your calculations. Always use
  462. ea_GetAttrs() and ea_SetAttrs() to read and write the attributes
  463. respectively.
  464.  
  465. Note that when the object is created, it is possible to pass border, size or
  466. minsize attributes. EAGUI protects these values, so your method can't change
  467. them, even if it tries to. All this is done automatically, so you don't need
  468. to worry about this.
  469.  
  470.  
  471.      Relations
  472.      ¯¯¯¯¯¯¯¯¯
  473. In some interfaces, you want to create special relations between objects. One
  474. example that comes to mind is a simple window with an "Ok" and a "Cancel"
  475. button. If these both have a fixed size, then one of them will be bigger than
  476. the other, simply because the label strings don't have the same width. This
  477. does not look very good. To correct this, you can add a relation between the
  478. two objects. All objects in a relation must have the same direct parent. This
  479. relation is added to the parent of the objects. For example:
  480.  
  481.      ea_NewRelation(obj_hgroup_ptr, &relhook,
  482.           EA_Object,          obj_okgad_ptr,
  483.           EA_Object,          obj_cancelgad_ptr,
  484.           TAG_DONE);
  485.  
  486. Any number of objects can be connected using one and the same relation. You
  487. can simply specify a list of objects by using the tag shown above. Relations
  488. also use the Hook mechanism. They're called after the minimum sizes were
  489. calculated, so you can read the minimum sizes of the objects in this method.
  490.  
  491. To explain how to create relations, a simple example is included below:
  492.  
  493.      /* same size relation */
  494.      ULONG rel_samesize(struct Hook *hook_ptr, struct List *list_ptr,
  495.           APTR msg_ptr)
  496.      {
  497.           struct ea_RelationObject *ro_ptr;
  498.           ULONG minx, miny;
  499.           ULONG x, y;
  500.  
  501.           minx = 0;
  502.           miny = 0;
  503.  
  504.           /* examine the list of objects that are affected by the relation */
  505.           ro_ptr = (struct ea_RelationObject *)list_ptr->lh_Head;
  506.           while (ro_ptr->node.ln_Succ)
  507.           {
  508.                ea_GetAttrs(ro_ptr->object_ptr,
  509.                     EA_MinWidth,        &x,
  510.                     EA_MinHeight,       &y,
  511.                     TAG_DONE);
  512.  
  513.                /* find the maximum values of the minimum sizes */
  514.                minx = MAX(x, minx);
  515.                miny = MAX(y, miny);
  516.  
  517.                ro_ptr = (struct ea_RelationObject *)ro_ptr->node.ln_Succ;
  518.           }
  519.  
  520.           /* set all objects to the newly found minimum sizes */
  521.           ro_ptr = (struct ea_RelationObject *)list_ptr->lh_Head;
  522.           while (ro_ptr->node.ln_Succ)
  523.           {
  524.                ea_SetAttrs(ro_ptr->object_ptr,
  525.                     EA_MinWidth,        minx,
  526.                     EA_MinHeight,       miny,
  527.                     TAG_DONE);
  528.  
  529.                ro_ptr = (struct ea_RelationObject *)ro_ptr->node.ln_Succ;
  530.           }
  531.           return(0);
  532.      }
  533.  
  534. The example is pretty self-explaining.
  535.  
  536.  
  537.      Custom Images
  538.      ¯¯¯¯¯¯¯¯¯¯¯¯¯
  539. Support for images is something new in Release 2 of the library. Because of
  540. the diversity of custom imagery, it still requires a fair amount of
  541. programming, but it is integrated with the rest of the interface, so it will
  542. probably save you work in the long run.
  543.  
  544. Basically, when you want to use a custom image, you have to do the following
  545. things:
  546.  
  547.  - Combine all extra attributes your image needs to render itself into one
  548.    custom structure, and use the EA_UserData tag to add this structure to the
  549.    custom image object.
  550.  - Write a method that determines the minimum size the image occupies.
  551.  - Write a method that renders the image.
  552.  
  553. Let's make a simple custom image that you'll probably need anyway: a text
  554. label image. The full code to this example can be found in "TextField.c".
  555. First, we'll define the attributes that this image needs to render itself.
  556. We need a pointer to the string that must be displayed, a pointer to a
  557. TextAttr structure, which determines the font we want to use, a field that
  558. contains some layout flags (which determine how the text is aligned within
  559. the object space) and a pen number, which determines which color will be used
  560. to render the text.
  561.  
  562. These are all combined in the ci_TextField structure, which is declared in
  563. the "TextField.c" source.
  564.  
  565. The next thing is a method that determines the minimum size of the object.
  566. This is pretty straightforward. All we have to do is determine the dimensions
  567. of the text. EAGUI provides two functions for that: ea_TextLength() and
  568. ea_TextHeight(). We simply use these to fill in the appropriate fields. Look
  569. at the source to see how this is implemented.
  570.  
  571. Finally, we have to write a method that actually renders the object on
  572. screen. This callback hook is new, and it is a bit different from the ones
  573. mentioned previously, because it uses the third hook parameter to pass some
  574. extra parameters. The ea_RenderMessage structure contains a pointer to the
  575. root object and a pointer to the RastPort that should be used for rendering.
  576. Why we need the root pointer might not be immediately obvious. Because the
  577. object itself only contains relative offsets, we need it to determine the
  578. position of the object in the window (ie relative to the root pointer). EAGUI
  579. provides two functions for that, which both need that pointer:
  580. ea_GetObjectLeft() and ea_GetObjectTop(). The actual rendering can then be
  581. performed. Take a look at the source. It is a pretty basic example, that can
  582. be expanded (for example to support an underlined hotkey, or even more fancy
  583. stuff).
  584.  
  585.  
  586.      Advanced Topics
  587.      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  588. This section contains a collection of specific advanced topics.
  589.  
  590. There is a known problem with GadTools. Connecting a string gadget to a
  591. listview gadget will not work. This is because the taglist of the listview
  592. gadget must contain a pointer to an already created string gadget. It is not
  593. possible to have these kind of dependencies. The only remedy is not to use
  594. this option, and glue the two gadgets together in your application. Fixing
  595. this problem is not easy, especially since it's a create-time only tag, so
  596. there's no way to make the connection afterwards.
  597.  
  598. Another strange thing, that you might have noticed, is that when you resize a
  599. window to a smaller dimension, GadTools will refresh the gadgets in their old
  600. state, and draw them over the window borders. In fact, this refresh is done
  601. if you make the window larger too, but it's harder to notice it in that case.
  602. This refresh is something that happens, before you're even notified of a size
  603. change, so there's not much you can do about it, unless you use the method
  604. described here, which has a drawback too.
  605.      As stated, when you receive the IDCMP_NEWSIZE message, it's already too
  606. late. However, if your program code allows you to use IDCMP_SIZEVERIFY
  607. messages, you can do this:
  608.  
  609.      case IDCMP_SIZEVERIFY:
  610.           RemoveGList(win_ptr, gadlist_ptr, -1);
  611.           ea_FreeGadgetList(winobj_ptr, gadlist_ptr);
  612.           gadlist_ptr = NULL;
  613.           break;
  614.  
  615.      Right after you've replied to this message, you'll receive a
  616. IDCMP_NEWSIZE message, where you do everything you normally do (except remove
  617. the gadget list, which you've already done of course). Before you use this
  618. method, make absolutely sure that you read the warnings in the AutoDocs about
  619. the verify messages. They can be found in the intuition.library/ModifyIDCMP()
  620. AutoDoc. Don't say we didn't warn you!
  621.  
  622.  
  623.      Bibliography
  624.      ¯¯¯¯¯¯¯¯¯¯¯¯
  625.      [1] The Amiga User Interface Style Guide, Addison Wesley
  626.  
  627.      [2] The Amiga ROM Kernel Reference Manuals, Addison Wesley
  628.